

#include "CGUIEditBox.h"
#include "IGUISkin.h"
#include "IGUIEnvironment.h"
#include "IGUIFont.h"
#include "IVideoDriver.h"
#include "rect.h"
#include "os.h"
#include "Keycodes.h"

#ifdef _IRR_PSP_
extern int key_state;
extern int key_type;

extern int wasStartDown;
#endif
namespace irr
{
	namespace gui
	{

		//! constructor
		CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment,
			IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
			IOSOperator* op)
			: IGUIEditBox(environment, parent, id, rectangle), Border(border), 
			OverrideColorEnabled(false), OverrideFont(0), CursorPos(0), ScrollPos(0),
			MarkBegin(0), MarkEnd(0), MouseMarking(false), Operator(op), Max(0)
		{
#ifdef _DEBUG
			setDebugName("CGUIEditBox");
#endif

			OverrideColor = video::SColor(101,255,255,255);
			Text = text;

			if (Operator)
				Operator->grab();
		}


		//! destructor
		CGUIEditBox::~CGUIEditBox()
		{
			if (OverrideFont)
				OverrideFont->drop();

			if (Operator)
				Operator->drop();
		}


		//! Sets another skin independent font.
		void CGUIEditBox::setOverrideFont(IGUIFont* font)
		{
			if (OverrideFont)
				OverrideFont->drop();

			OverrideFont = font;

			if (OverrideFont)
				OverrideFont->grab();
		}


		//! Sets another color for the text.
		void CGUIEditBox::setOverrideColor(video::SColor color)
		{
			OverrideColor = color;
			OverrideColorEnabled = true;
		}


		//! Sets if the text should use the overide color or the
		//! color in the gui skin.
		void CGUIEditBox::enableOverrideColor(bool enable)
		{
			OverrideColorEnabled = enable;
		}


		//! called if an event happened.
		bool CGUIEditBox::OnEvent(SEvent event)
		{
			switch(event.EventType)
			{
			case EET_GUI_EVENT:
				if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
				{
					MouseMarking = false;
					MarkBegin = 0;
					MarkEnd = 0;
					return true;
				}
				break;
			case EET_KEY_INPUT_EVENT:
				return processKey(event);
				break;
			case EET_MOUSE_INPUT_EVENT:
				return processMouse(event);
				break;
			}

			return Parent ? Parent->OnEvent(event) : false;
		}


		bool CGUIEditBox::processKey(SEvent& event)
		{
#ifdef _IRR_PSP_
			if (event.KeyInput.PressedOnce)
			{
				if (event.KeyInput.Key == KEY_START && (wasStartDown == 0)) { key_type = 0; key_state = 1; }
			}
#endif
			if (!event.KeyInput.PressedDown)
				return false;

			// control shortcut handling

			if (event.KeyInput.Control)
			{
				switch(event.KeyInput.Key)
				{
				case KEY_KEY_C:
					// copy to clipboard
					if (Operator && MarkBegin != MarkEnd)
					{
						s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
						s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

						core::stringc s;
						s = Text.subString(realmbgn, realmend - realmbgn).c_str();
						Operator->copyToClipboard(s.c_str());
					}
					break;
				case KEY_KEY_X:
					// cut to the clipboard
					if (Operator && MarkBegin != MarkEnd)
					{
						s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
						s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

						// copy
						core::stringc sc;
						sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
						Operator->copyToClipboard(sc.c_str());

						// delete
						core::stringw s;
						s = Text.subString(0, realmbgn);
						s.append( Text.subString(realmend, Text.size()-realmend) );
						Text = s;

						CursorPos = realmbgn;
						MarkBegin = 0;
						MarkEnd = 0;
					}
					break;
				case KEY_KEY_V:
					// paste from the clipboard
					if (Operator)
					{
						s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
						s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

						// add new character
						c8* p = Operator->getTextFromClipboard();
						if (p)
						{
							if (MarkBegin == MarkEnd)
							{
								// insert text
								core::stringw s = Text.subString(0, CursorPos);
								s.append(p);
								s.append( Text.subString(CursorPos, Text.size()-CursorPos) );

								if (!Max || s.size()<=Max) // thx to Fish FH for fix
								{
									Text = s;
									s = p;
									CursorPos += s.size();
								}
							}
							else
							{
								// replace text

								core::stringw s = Text.subString(0, realmbgn);
								s.append(p);
								s.append( Text.subString(realmend, Text.size()-realmend) );

								if (!Max || s.size()<=Max)  // thx to Fish FH for fix
								{
									Text = s;
									s = p;
									CursorPos = realmbgn + s.size();
								}
							}
						}

						MarkBegin = 0;
						MarkEnd = 0;
					}
					break;
				default:
					return false;
				}
			}

			// default keyboard handling

			if (!event.KeyInput.Control)
				switch(event.KeyInput.Key)
			{
				case KEY_END:
					if (event.KeyInput.Shift)
					{
						if (MarkBegin == MarkEnd)
							MarkBegin = CursorPos;
						MarkEnd = Text.size();
					}
					else
					{
						MarkBegin = 0;
						MarkEnd = 0;
					}
					CursorPos = Text.size();
					BlinkStartTime = os::Timer::getTime();
					break;
				case KEY_HOME:
					if (event.KeyInput.Shift)
					{
						if (MarkBegin == MarkEnd)
							MarkBegin = CursorPos;
						MarkEnd = 0;
					}
					else
					{
						MarkBegin = 0;
						MarkEnd = 0;
					}
					CursorPos = 0;
					BlinkStartTime = os::Timer::getTime();
					break;
				case KEY_START:
					{
						SEvent e;
						e.EventType = EET_GUI_EVENT;
						e.GUIEvent.Caller = this;
						e.GUIEvent.EventType = EGET_EDITBOX_ENTER;
						Parent->OnEvent(e);
					}
					break;
				case KEY_LEFT:

					if (event.KeyInput.Shift)
					{
						if (CursorPos > 0) 
						{ 
							if (MarkBegin == MarkEnd) 
								MarkBegin = CursorPos; 

							MarkEnd = CursorPos-1; 
						}
					}
					else
					{
						MarkBegin = 0;
						MarkEnd = 0;
					}

					if (CursorPos > 0) CursorPos--;
					BlinkStartTime = os::Timer::getTime();
					break;

				case KEY_RIGHT:

					if (event.KeyInput.Shift) 
					{ 
						if (Text.size() > CursorPos) 
						{ 
							if (MarkBegin == MarkEnd) 
								MarkBegin = CursorPos; 

							MarkEnd = CursorPos+1; 
						} 
					}
					else
					{
						MarkBegin = 0;
						MarkEnd = 0;
					}

					if (Text.size() > CursorPos) CursorPos++;
					BlinkStartTime = os::Timer::getTime();
					break;

				case KEY_BACK:
					if (Text.size()!=0)
					{
						core::stringw s;

						if (MarkBegin != MarkEnd)
						{
							// delete marked text
							s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
							s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

							s = Text.subString(0, realmbgn);
							s.append( Text.subString(realmend, Text.size()-realmend) );
							Text = s;

							CursorPos = realmbgn;
						}
						else
						{
							// delete text behind cursor
							s = Text.subString(0, CursorPos-1);
							s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
							Text = s;
							--CursorPos;
						}

						if (CursorPos < 0)
							CursorPos = 0;
						BlinkStartTime = os::Timer::getTime();
						MarkBegin = 0;
						MarkEnd = 0;
					}
					break;
				case KEY_DELETE:
					if (Text.size() != 0)
					{
						core::stringw s;

						if (MarkBegin != MarkEnd)
						{
							// delete marked text
							s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
							s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

							s = Text.subString(0, realmbgn);
							s.append( Text.subString(realmend, Text.size()-realmend) );
							Text = s;

							CursorPos = realmbgn;
						}
						else
						{
							// delete text before cursor
							s = Text.subString(0, CursorPos);
							s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
							Text = s;
						}

						if (CursorPos > Text.size())
							CursorPos = Text.size();

						BlinkStartTime = os::Timer::getTime();
						MarkBegin = 0;
						MarkEnd = 0;
					}
					break;
				default:
					if (event.KeyInput.Char != 0)
					{
						if (Text.size() < Max || Max == 0)
						{
							core::stringw s;

							if (MarkBegin != MarkEnd)
							{
								// replace marked text
								s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
								s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

								s = Text.subString(0, realmbgn);
								s.append(event.KeyInput.Char);
								s.append( Text.subString(realmend, Text.size()-realmend) );
								Text = s;
								CursorPos = realmbgn+1;
							}
							else
							{
								// add new character
								s = Text.subString(0, CursorPos);
								s.append(event.KeyInput.Char);
								s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
								Text = s;
								++CursorPos;
							}

							BlinkStartTime = os::Timer::getTime();
							MarkBegin = 0;
							MarkEnd = 0;
						}
					}
					break;
			}

			// calculate scrollpos

			IGUIFont* font = OverrideFont;
			IGUISkin* skin = Environment->getSkin();
			if (!OverrideFont)
				font = skin->getFont();

			s32 cursorwidth = font->getDimension(L"_ ").Width;

			s32 minwidht = cursorwidth*2;
			if (minwidht >= AbsoluteRect.getWidth())
				minwidht = AbsoluteRect.getWidth() / 2;

			s32 tries = Text.size()*2;
			if (tries < 100)
				tries = 100;

			for (s32 t=0; t<tries; ++t)
			{
				core::stringw s = Text.subString(0,CursorPos);
				s32 charcursorpos = font->getDimension(s.c_str()).Width;

				s = Text.subString(0, ScrollPos);
				s32 charscrollpos = font->getDimension(s.c_str()).Width;

				if ((charcursorpos + cursorwidth - charscrollpos) > AbsoluteRect.getWidth())
					ScrollPos++;
				else
					if ((charcursorpos + cursorwidth - charscrollpos) < minwidht)
					{
						if (ScrollPos > 0)
							ScrollPos--;
						else
							break;
					}
					else
						break;
			}

			return true;
		}

		//! draws the element and its children
		void CGUIEditBox::draw()
		{
			if (!IsVisible)
				return;

			bool focus = Environment->hasFocus(this);

			IGUISkin* skin = Environment->getSkin();
			if (!skin)
				return;

			irr::video::IVideoDriver* driver = Environment->getVideoDriver();

			core::rect<s32> frameRect(AbsoluteRect);

			// draw the border

			if (Border)
			{
				skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW),
					false, true, frameRect, &AbsoluteClippingRect);

				frameRect.UpperLeftCorner.X += 3;
			}

			// draw the text

			IGUIFont* font = OverrideFont;
			if (!OverrideFont)
				font = skin->getFont();

			if (font)
			{
				// calculate cursor pos

				core::stringw s = Text.subString(0,CursorPos);
				s32 charcursorpos = font->getDimension(s.c_str()).Width;

				s = Text.subString(0, ScrollPos);
				s32 charscrollpos = font->getDimension(s.c_str()).Width;

				core::rect<s32> rct;

				// draw mark

				if (focus && MarkBegin != MarkEnd)
				{
					rct = frameRect;

					rct.LowerRightCorner.Y -= 2;
					rct.UpperLeftCorner.Y += 2;

					s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
					s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

					s = Text.subString(0, realmbgn);
					s32 mbegin = font->getDimension(s.c_str()).Width;

					s = Text.subString(realmbgn, realmend - realmbgn);
					s32 mend = font->getDimension(s.c_str()).Width;

					rct.UpperLeftCorner.X  += mbegin - charscrollpos;
					rct.LowerRightCorner.X = rct.UpperLeftCorner.X + mend;

					driver->draw2DRectangle(skin->getColor(EGDC_HIGH_LIGHT), rct, &AbsoluteClippingRect);
				}

				// draw cursor

				if (focus && (os::Timer::getTime() - BlinkStartTime) % 700 < 350)
				{
					rct = frameRect;
					rct.UpperLeftCorner.X += charcursorpos;
					rct.UpperLeftCorner.X -= charscrollpos;

					font->draw(L"_", rct, 
						OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
						false, true, &AbsoluteClippingRect);
				}

				// draw text

				if (Text.size())
				{
					rct = frameRect;
					rct.UpperLeftCorner.X -= charscrollpos;

					if (focus && MarkBegin != MarkEnd)
					{
						// marked text

						font->draw(Text.c_str(), rct, 
							OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
							false, true, &AbsoluteClippingRect);

						s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
						s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

						s = Text.subString(0, realmbgn);
						s32 mbegin = font->getDimension(s.c_str()).Width;

						s = Text.subString(realmbgn, realmend - realmbgn);

						rct.UpperLeftCorner.X += mbegin;

						font->draw(s.c_str(), rct, 
							OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
							false, true, &AbsoluteClippingRect);								
					}
					else
					{
						// normal text
						font->draw(Text.c_str(), rct, 
							OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
							false, true, &AbsoluteClippingRect);
					}
				}
			}
		}

		//! Sets the new caption of this element.
		void CGUIEditBox::setText(const wchar_t* text)
		{
			Text = text;
			CursorPos = 0;
			ScrollPos = 0;
			MarkBegin = 0;
			MarkEnd = 0;
		}


		//! Sets the maximum amount of characters which may be entered in the box.
		//! \param max: Maximum amount of characters. If 0, the character amount is 
		//! infinity.
		void CGUIEditBox::setMax(s32 max)
		{
			Max = max;
			if (Max < 0)
				Max = 0;

			if (Text.size() > Max && Max != 0)
				Text = Text.subString(0, Max);
		}


		//! Returns maximum amount of characters, previously set by setMax();
		s32 CGUIEditBox::getMax()
		{
			return Max;
		}


		bool CGUIEditBox::processMouse(SEvent& event)
		{
			switch(event.MouseInput.Event)
			{
			case irr::EMIE_LMOUSE_LEFT_UP:
				if (Environment->hasFocus(this))
				{
					CursorPos = getCursorPos(event.MouseInput.X);
					if (MouseMarking)
						MarkEnd = CursorPos;
					MouseMarking = false;			
					return true;
				}
				break;
			case irr::EMIE_MOUSE_MOVED:
				{
					if (MouseMarking)
					{
						CursorPos = getCursorPos(event.MouseInput.X);
						MarkEnd = CursorPos;
						return true;
					}
				}
				break;
			case EMIE_LMOUSE_PRESSED_DOWN:
				if (!Environment->hasFocus(this))
				{
					// get focus
					BlinkStartTime = os::Timer::getTime();
					Environment->setFocus(this);
					MouseMarking = true;
					CursorPos = getCursorPos(event.MouseInput.X);
					MarkBegin = CursorPos;
					MarkEnd = CursorPos;
					return true;
				}
				else
				{
					if (!AbsoluteRect.isPointInside(
						core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y)))
					{
						// remove focus
						Environment->removeFocus(this);
						return false;
					}

					// move cursor

					CursorPos = getCursorPos(event.MouseInput.X);

					if (!MouseMarking)
						MarkBegin = CursorPos;

					MouseMarking = true;
					MarkEnd = CursorPos;
					return true;
				}	
			}

			return false;
		}


		s32 CGUIEditBox::getCursorPos(s32 x)
		{
			IGUIFont* font = OverrideFont;
			IGUISkin* skin = Environment->getSkin();
			if (!OverrideFont)
				font = skin->getFont();

			core::stringw s = Text.subString(0, ScrollPos);
			s32 charscrollpos = font->getDimension(s.c_str()).Width;

			s32 idx = font->getCharacterFromPos(Text.c_str(), x - (AbsoluteRect.UpperLeftCorner.X + 3) + charscrollpos);	
			if (idx != -1)
				return idx;		

			return Text.size();
		}


	} // end namespace gui
} // end namespace irr

